"""将图片转换成C语言格式的数组"""
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
import os
import re

from RLE16_compress import compress_rle16
from RLE8_compress import compress_rle8


def swap16(val):
    """交换高低字节顺序"""
    return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)


def rgb888_to_rgb565(r, g, b):
    val = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
    return val


def rgb888_to_rgb555(r, g, b):
    val = ((r & 0xF8) << 7) | ((g & 0xF8) << 2) | (b >> 3)
    return val


def get_position_str(i, width, height, scan_dir):
    if scan_dir == "行优先":
        xPos, yPos = i % width, i // width
    else:
        xPos, yPos = i // height, i % height
    return f"@ {xPos:3d},{yPos:3d}"


def format_rle16_output(rle_data, width, height, scan_dir):
    """
    格式化RLE16压缩输出
    """
    arr = []
    n = len(rle_data)
    i = 0
    while i < n:
        position_str = get_position_str(i, width, height, scan_dir)

        # 检查是否是RLE数据（高位为1）
        if rle_data[i] & 0x8000:
            # RLE数据: [长度|0x8000, 值]
            rle_value = rle_data[i+1]
            rle_len = (rle_data[i] & 0x7FFF)
            line_len = rle_len
            rle_str = ", ".join([f"0x8000 + {rle_len},0x{rle_value:04X},"])
            i += 2
            while i < n and rle_data[i] & 0x8000 and rle_value == rle_data[i+1]:
                rle_len = (rle_data[i] & 0x7FFF)
                line_len += rle_len
                rle_str += ", ".join([f" 0x{0x8000 + rle_len:04X},0x{rle_value:04X},"])
                i += 2
            arr.append(f"/* RLE:{line_len:5d} {position_str} */ {rle_str}")

        else:
            # 非RLE数据: [长度, 数据1, 数据2, ...]
            lit_len = rle_data[i]
            literal_str = ", ".join(f"0x{rle_data[i+1+j]:04X}" for j in range(lit_len))
            arr.append(f"/* ABS:{lit_len:5d} {position_str} */ {lit_len}, {literal_str},")
            i += 1 + lit_len

    return arr


def format_rle8_output(rle_data, width, height, scan_dir):
    """
    格式化RLE8压缩输出
    """
    arr = []
    n = len(rle_data)
    i = 0
    while i < n:
        position_str = get_position_str(i, width, height, scan_dir)

        if rle_data[i] == 255:
            arr.append("255 // 结束")
            i += 1
        elif rle_data[i] == 0:
            lit_len = rle_data[i+1]
            literal_str = ", ".join(f"0x{rle_data[i+2+j]:02X}" for j in range(lit_len))
            arr.append(f"/* ABS:{lit_len:5d} {position_str} */ 0, {lit_len}, {literal_str},")
            i += 2 + lit_len
        else:
            # RLE数据
            rle_len = (rle_data[i])
            rle_value = rle_data[i+1]
            rle_str = ", ".join([f"{rle_data[i]}, 0x{rle_data[i+1]:02X},"])
            line_len = rle_len
            i += 2
            while i < n and rle_data[i] != 255 and rle_data[i] != 0 and rle_value == rle_data[i+1]:
                rle_len = rle_data[i]
                line_len += rle_len
                rle_str += "".join([f" 0x{rle_data[i]:02X}, 0x{rle_data[i+1]:02X},"])
                i += 2
            arr.append(f"/* RLE:{line_len:5d} {position_str} */ {rle_str}")

            # arr.append(f" /* RLE:{rle_data[i]} {position_str} */ {rle_data[i]}, 0x{rle_data[i+1]:02X},")
            # i += 2

    return arr


def format_uncompressed_output(pixels, width, height, scan_dir):
    """
    格式化未压缩输出
    """
    line_item_set = 8  # 每行排列8个元素
    arr = []
    for i in range(0, len(pixels), line_item_set):
        position_str = get_position_str(i, width, height, scan_dir)

        line_str = [f" 0x{v:04X}," for v in pixels[i:i+line_item_set]]
        arr.append(f"/* @{position_str} */" + "".join(line_str))

    return arr


def convert_pixels(img, color_fmt, scan_dir, byte_order):
    """
    将图像转换为像素值列表
    """
    width, height = img.size
    pixels = []
    data = img.convert("RGB").load()

    if scan_dir == "行优先":
        for y in range(height):
            for x in range(width):
                r, g, b = data[x, y]
                if color_fmt == "RGB565":
                    val = rgb888_to_rgb565(r, g, b)
                else:
                    val = rgb888_to_rgb555(r, g, b)

                if byte_order == "BE":
                    val = swap16(val)

                pixels.append(val)
    else:  # 列优先
        for x in range(width):
            for y in range(height):
                r, g, b = data[x, y]
                if color_fmt == "RGB565":
                    val = rgb888_to_rgb565(r, g, b)
                else:
                    val = rgb888_to_rgb555(r, g, b)
                if byte_order == "BE":
                    val = swap16(val)
                pixels.append(val)

    return pixels, width, height


def image_to_c_array(img, color_fmt, compress, scan_dir, array_name, byte_order, src_path, rle_mode="RLE16"):
    # 转换像素
    pixels, width, height = convert_pixels(img, color_fmt, scan_dir, byte_order)

    # 处理压缩和输出格式
    arr = []
    if compress:
        if rle_mode == "RLE8":
            # RLE8压缩
            rle_data = compress_rle8(pixels)
            arr = format_rle8_output(rle_data, width, height, scan_dir)
            compress_note = "RLE8压缩（0表示原始内容，1~254为游程长度，255为结束，数据为uint8_t，颜色低8位）"
            c_type = "unsigned char"
        else:
            # RLE16压缩
            rle_data = compress_rle16(pixels)
            arr = format_rle16_output(rle_data, width, height, scan_dir)
            compress_note = "RLE16"
            c_type = "unsigned short"
    else:
        # 无压缩
        arr = format_uncompressed_output(pixels, width, height, scan_dir)
        compress_note = "无压缩"
        c_type = "unsigned short"

    c_array = "/**\n"
    c_array += f" 图片文件: {src_path}\n"
    c_array += f" 图像尺寸: Width={width}, Height={height}\n"
    c_array += f" 生成选项: 颜色格式={color_fmt}, 扫描方向={scan_dir}, 字节序={byte_order}\n"
    c_array += f" 压缩格式: {compress_note}\n"
    c_array += "*/\n"
    c_array += f"const {c_type} {array_name}[] = {{\n "
    c_array += "\n ".join(arr)
    c_array += "\n};\n"
    c_array += f"// 压缩项数: {len(arr)}\n"
    return c_array


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("图片转C数组工具")
        self.img = None
        self.img_path = ""
        self.array_name = "image_array"
        self.color_fmt = tk.StringVar(value="RGB565")
        self.compress = tk.BooleanVar(value=True)
        self.scan_dir = tk.StringVar(value="行优先")
        self.byte_order = tk.StringVar(value="LE")
        self.scale = tk.DoubleVar(value=1.0)
        self.rle_mode = tk.StringVar(value="RLE16")
        self.canvas_width = 320
        self.canvas_height = 240

        # 主frame
        main_frame = tk.Frame(root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)

        # 左侧：图片显示与信息
        left_frame = tk.Frame(main_frame)
        left_frame.grid(row=0, column=0, sticky="nsew")
        left_frame.grid_rowconfigure(0, weight=1)
        left_frame.grid_columnconfigure(0, weight=1)
        self.canvas = tk.Canvas(left_frame, width=self.canvas_width, height=self.canvas_height, bg="gray")
        self.canvas.grid(row=0, column=0, sticky="nsew")
        self.info_label = tk.Label(left_frame, text="图片信息", anchor="w", justify="left")
        self.info_label.grid(row=1, column=0, sticky="ew", pady=4)

        # 右侧：配置与操作
        right_frame = tk.LabelFrame(main_frame, text="生成选项")
        right_frame.grid(row=0, column=1, sticky="nsew", padx=(12, 0))
        right_frame.grid_rowconfigure(8, weight=1)
        right_frame.grid_columnconfigure(1, weight=1)

        tk.Button(right_frame, text="选择图片", command=self.open_image).grid(row=0, column=0, columnspan=2, sticky="ew", pady=2)
        tk.Label(right_frame, text="缩放:").grid(row=1, column=0, sticky="e")
        tk.Scale(right_frame, variable=self.scale, from_=0.1, to=10.0, resolution=0.1, orient=tk.HORIZONTAL, command=self.show_image).grid(row=1, column=1, sticky="ew")
        tk.Label(right_frame, text="颜色格式:").grid(row=2, column=0, sticky="e")
        tk.OptionMenu(right_frame, self.color_fmt, "RGB565", "RGB555").grid(row=2, column=1, sticky="ew")
        tk.Label(right_frame, text="压缩:").grid(row=3, column=0, sticky="e")
        tk.Checkbutton(right_frame, variable=self.compress, text="启用压缩").grid(row=3, column=1, sticky="w")
        tk.Label(right_frame, text="压缩类型:").grid(row=4, column=0, sticky="e")
        tk.OptionMenu(right_frame, self.rle_mode, "RLE16", "RLE8").grid(row=4, column=1, sticky="ew")
        tk.Label(right_frame, text="扫描方向:").grid(row=5, column=0, sticky="e")
        tk.OptionMenu(right_frame, self.scan_dir, "行优先", "列优先").grid(row=5, column=1, sticky="ew")
        tk.Label(right_frame, text="字节序:").grid(row=6, column=0, sticky="e")
        tk.OptionMenu(right_frame, self.byte_order, "LE", "BE").grid(row=6, column=1, sticky="ew")
        tk.Button(right_frame, text="生成C数组", command=self.generate_c_array, height=2, font=("微软雅黑", 12, "bold")).grid(row=7, column=0, columnspan=2, sticky="ew", pady=12)

        # 使canvas随窗口缩放
        left_frame.grid_rowconfigure(0, weight=1)
        left_frame.grid_columnconfigure(0, weight=1)
        main_frame.grid_rowconfigure(0, weight=1)
        main_frame.grid_columnconfigure(0, weight=1)
        main_frame.grid_columnconfigure(1, weight=0)
        self.canvas.bind("<Configure>", self.on_canvas_resize)

    def open_image(self):
        path = filedialog.askopenfilename(filetypes=[("图片文件", "*.bmp;*.png;*.jpg;*.jpeg")])
        if not path:
            return
        self.img_path = path
        self.img = Image.open(path)
        info = f"文件: {os.path.basename(path)}\n尺寸: {self.img.width}x{self.img.height}\n模式: {self.img.mode}"
        self.info_label.config(text=info)
        self.show_image()

    def show_image(self, event=None):
        if not self.img:
            return
        canvas_w = self.canvas.winfo_width()
        canvas_h = self.canvas.winfo_height()
        scale_val = self.scale.get()
        w = int(self.img.width * scale_val)
        h = int(self.img.height * scale_val)
        # 限制最大显示尺寸不超过canvas
        if w > canvas_w or h > canvas_h:
            ratio = min(canvas_w / w, canvas_h / h)
            w = int(w * ratio)
            h = int(h * ratio)
        img_disp = self.img.copy()
        img_disp = img_disp.resize((w, h), Image.LANCZOS)
        self.tkimg = ImageTk.PhotoImage(img_disp)
        self.canvas.delete("all")
        self.canvas.create_image(canvas_w // 2, canvas_h // 2, image=self.tkimg)

    def on_canvas_resize(self, event):
        self.show_image()

    def generate_c_array(self):
        if not self.img:
            messagebox.showerror("错误", "请先选择图片文件")
            return
        image_name = os.path.splitext(os.path.basename(self.img_path))[0]
        array_name = '_' + re.sub(r'[^a-zA-Z0-9_]', '_', image_name) + "_array.c"
        c_code = image_to_c_array(self.img, self.color_fmt.get(), self.compress.get(), self.scan_dir.get(), array_name, self.byte_order.get(), self.img_path, self.rle_mode.get())
        out_name = image_name + "_array.c"
        with open(out_name, "w", encoding="utf-8") as f:
            f.write(c_code)
        messagebox.showinfo("完成", f"C数组已保存到 {out_name}")


if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()
